#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>

#include <readconf.h>
#include <hostmap.h>

#include "collector.h"

int main(int argc, char **argv)
{
	int ssd, csd;
	struct sockaddr_in saddr, caddr;
	socklen_t clen;
	pid_t	child_pid;
	struct sigaction sa;
	struct msg_host_opt_st *opt;
	int opt_size;
	int ret;

	/*
	** 测试使用，获取主机名表。
	*/
	init_hostmap();
	read_host_conf();


	sa.sa_handler = chld_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_NOCLDSTOP;

	ssd = socket(AF_INET, SOCK_STREAM, 0);
	if (ssd == -1) {
		perror("socket()");
		exit(1);
	}

	memset(&saddr, 0, sizeof(saddr));
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(get_opt_port());
	saddr.sin_addr.s_addr = htonl(INADDR_ANY);
	ret = bind(ssd, (struct sockaddr *)&saddr, sizeof(saddr));
	if (ret == -1) {
		perror("bind()");
		exit(1);
	}

	ret = listen(ssd, 255);
	if (ret == -1) {
		perror("listen()");
		exit(1);
	}

	sigaction(SIGCHLD, &sa, NULL);

	opt = NULL;
	opt_size = 0;
	while (1) {
		clen = sizeof(caddr);
		csd = accept(ssd, (struct sockaddr *)&caddr, &clen);
		if (csd == -1) {
			if (errno == EINTR) {
				continue;
			}
			perror("accept()");
			continue;
		}

		child_pid = fork();
		if (child_pid == -1) {
			perror("fork()");
			continue;
		}
		if (child_pid == 0) {
			close(ssd);

			ret = read_remote_log(csd, &opt, &opt_size);
			if (ret == -1) {
				free_opt(&opt, opt_size);
				close(csd);
				exit(1);
			}
			ret = save_remote_log(&opt, &opt_size);
			if (ret == -1) {
				free_opt(&opt, opt_size);
				close(csd);
				exit(1);
			}

			free_opt(&opt, opt_size);
			close(csd);
			exit(0);
		} else {
			close(csd);
		}
	}

	free_opt(&opt, opt_size);
	close(ssd);

	return 0;
}

/*读取Agent传来的日志信息。*/
static int read_remote_log(int sockfd, struct msg_host_opt_st **opt, int *opt_size)
{
	void *retc;
	//struct tm *t;
	//time_t tm;
	int ret, pos, len;

	if (*opt_size < sizeof(**opt)) {
		*opt_size = sizeof(**opt);
	}
	retc = realloc(*opt, *opt_size);
	if (retc == NULL) {
		if (*opt != NULL) {
			free(*opt);
		}
		return -1;
	}
	*opt = retc;

	len = sizeof(**opt);
	pos = 0;
	while (len > 0) {
		ret = read(sockfd, *opt + pos, len);
		if (ret == -1) {
			if (errno == EINTR) {
				continue;
			}
			perror("read()");
			free(*opt);
			return -1;
		}
		pos += ret;
		len -= ret;
	}

	if (*opt_size < sizeof(**opt) + ntohl((*opt)->offset)) {
		*opt_size = sizeof(**opt) + ntohl((*opt)->offset);
	}
	retc = realloc(*opt, *opt_size);
	if (retc == NULL) {
		if (*opt != NULL) {
			free(*opt);
		}
		return -1;
	}
	*opt = retc;

	len = ntohl((*opt)->offset);
	pos = 0;
	while (len > 0) {
		ret = read(sockfd, &((*opt)->data[1]) + pos, len);
		if (ret == -1) {
			if (errno == EINTR) {
				continue;
			}
			perror("read()");
			free(*opt);
			return -1;
		}
		pos += ret;
		len -= ret;
	}

/*
	tm = ntohl((*opt)->time);
	t = localtime((time_t *)&tm);
	printf("时间：%d/%d/%d %d:%d:%d\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
	printf("opt:%d\t%d\n", (*opt)->opt, (*opt)->data[0]);
	printf("action:%s\n", &(*opt)->data[1]);
*/

	return 0;
}

/*释放结构体struct msg_host_opt_st指针*/
static void free_opt(struct msg_host_opt_st **opt, const int opt_size)
{
	if (*opt != NULL) {
		free(*opt);
	}

	return;
}

/*将日志保存在server端，并入库。*/
static int save_remote_log(struct msg_host_opt_st **opt, int *opt_size)
{
	FILE *fp;
	static char log_path[PATH_MAX_LEN];
	static char host_opt_lv[LV_MAX_LEN];
	static char hostname[HOST_MAX_LEN];
	static char timestr[TIME_STR_LEN];
	static char optstr1[OPT_STR_LEN];
	static char optstr2[OPT_STR_LEN];
	static struct tm *t;
	static time_t time;

	/*	TODO
		获取主机名模块，需加入get_hostname.h，待实现。
		函数原型：int get_hostname(const int host_map, char *hostname, int len);
	ret = get_hostname(host_map, hostname, HOST_MAX_LEN);
	*/

	struct host_info hinfo;
	get_hostinfo((*opt)->host_map, &hinfo);
	strncpy(hostname, hinfo.hostname, HOST_MAX_LEN);

	/*	TODO
		检测日志文件是否超过了10M，如果超过了则备份，然后总新打开一个文件，用stat函数。
		需要做成一个函数，和Linux系统日志的跟新模式相同，最多保存10个日志文件（log.10）。
	*/
	snprintf(log_path, PATH_MAX_LEN, LOG_PATH""LOG_REMOTE, hostname);
	fp = fopen(log_path, "a+");
	if (fp == NULL) {
		perror("fopen()");
		return -1;
	}

	/*	!!!去掉此模块，状态检测在Agent端执行，并通过Socket传过来。
		检测主机状态函数，属于本模块
		函数原型：int check_host_status(const struct msg_host_opt_st *opt, char *host_opt_lv, int len);
	ret = check_host_status(*opt, host_opt_lv, LV_MAX_LEN);
	*/
	switch ((*opt)->data[0]) {
		case OPT_LV_OK:
			strncpy(host_opt_lv, OPT_STR_LV_OK, LV_MAX_LEN);
			break;
		case OPT_LV_WARN:
			strncpy(host_opt_lv, OPT_STR_LV_WARN, LV_MAX_LEN);
			break;
		case OPT_LV_ERROR:
			strncpy(host_opt_lv, OPT_STR_LV_ERROR, LV_MAX_LEN);
			break;
		default:
			break;
	}

	time = ntohl((*opt)->time);
	t = localtime((time_t *)&time);
	snprintf(timestr, TIME_STR_LEN, TIME_FORMAT, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);

	switch ((*opt)->opt[0]) {
		case OPT_POWER:
			strncpy(optstr1, OPT_STR_POWER, OPT_STR_LEN);
			break;
		case OPT_SHUTDOWN:
			strncpy(optstr1, OPT_STR_SHUTDOWN, OPT_STR_LEN);
			break;
		case OPT_LOGOUT:
			strncpy(optstr1, OPT_STR_LOGOUT, OPT_STR_LEN);
			break;
		case OPT_RUN:
			strncpy(optstr1, OPT_STR_RUN, OPT_STR_LEN);
			break;
		case OPT_SURF:
			strncpy(optstr1, OPT_STR_SURF, OPT_STR_LEN);
			break;
		default:
			printf("(*opt)->opt[0] error.\n");
			break;
	}
	switch ((*opt)->opt[1]) {
		case 0:
			break;
		case OPT_OPEN:
			strncpy(optstr2, OPT_STR_OPEN, OPT_STR_LEN);
			break;
		case OPT_CLOSE:
			strncpy(optstr2, OPT_STR_CLOSE, OPT_STR_LEN);
			break;
		default:
			printf("(*opt)->opt[1] error.\n");
			break;
	}

	//snprintf(logstr, LOG_MAX_LEN, LOG_FORMAT, host_opt_lv, timestr, hostname, optstr, &(*opt->data[1]));
	if (ntohl((*opt)->offset) == 0) {
		fprintf(fp, LOG_FORMAT, host_opt_lv, timestr, hostname, optstr1, optstr2, "");
	} else {
		fprintf(fp, LOG_FORMAT, host_opt_lv, timestr, hostname, optstr1, optstr2, &((*opt)->data[1]));
	}

	return 0;
}

static void chld_handler(int s)
{
	while (waitpid(-1, NULL, WNOHANG)>0);

	return;
}

